﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Kwan.Data
{
    /// <summary>
    /// 条件表达式
    /// </summary>
    [Serializable]
    public partial class Expression
    {
        public Expression() 
        {
            
        }
        private List<Command.Parameter> mParameters = new List<Command.Parameter>();
        public List<Command.Parameter> Parameters
        {
            get
            {
                return mParameters;
            }
        }
        private StringBuilder mSqlText = new StringBuilder();
        public StringBuilder SqlText
        {
            get
            {
                return mSqlText;
            }
             
        }
        internal void Parse(Command cmd)
        {
            if (SqlText.Length > 0)
            {
                cmd.Text.Append(" where " + SqlText.ToString());
                foreach (Command.Parameter p in Parameters)
                {
                    cmd.AddParameter(p);
                }
            }
        }
        public static Expression operator &(Expression exp1, Expression exp2)
        {
            if (exp1 == null || exp1.SqlText.Length == 0)
                return exp2;
            if (exp2 == null || exp2.SqlText.Length == 0)
                return exp1;

            Expression exp = new Expression();
            exp.SqlText.Append("(");
            exp.SqlText.Append(exp1.ToString());

            exp.SqlText.Append(")");
            exp.Parameters.AddRange(exp1.Parameters);
            exp.SqlText.Append(" and (");
            exp.SqlText.Append(exp2.SqlText.ToString());
            exp.SqlText.Append(")");
            exp.Parameters.AddRange(exp2.Parameters);
            return exp;
        }
        public static Expression operator |(Expression exp1, Expression exp2)
        {
            if (exp1 == null || exp1.SqlText.Length == 0)
                return exp2;
            if (exp2 == null || exp2.SqlText.Length == 0)
                return exp1;
            Expression exp = new Expression();
            exp.SqlText.Append("(");
            exp.SqlText.Append(exp1.ToString());
           
            exp.SqlText.Append(")");
            exp.Parameters.AddRange(exp1.Parameters);
            exp.SqlText.Append(" or (");
            exp.SqlText.Append(exp2.SqlText.ToString());
            exp.SqlText.Append(")");
            exp.Parameters.AddRange(exp2.Parameters);
            return exp;

        }
        internal static string GetParamName()
        {
          

                ParamNameSeed pns = NameSeed;
                if (pns.Value > 200)
                    pns.Value = 0;
                else
                    pns.Value++;
                return "p" + pns.Value;
           
          
        }
        [ThreadStatic]
        static ParamNameSeed mNameSeed = new ParamNameSeed();
        internal static ParamNameSeed NameSeed
        {
            get
            {
                if (mNameSeed == null)
                    mNameSeed = new ParamNameSeed();
                return mNameSeed;
            }
        }
        internal class ParamNameSeed
        {
            public int Value
            {
                get;
                set;
            }
        }
        public override string ToString()
        {

            return SqlText.ToString();
        }
        
    }
    public class FieldInfo
    {
        public FieldInfo(string table, string name)
        {
            DBContext.Init();
            mTable = table;
            mName = name;
        }
        private string mTable;
        public string Table
        {
            get
            {
                return mTable;
            }
        }
        private string mName;
        public string Name
        {
            get
            {
                return mName;
            }
        }
        public Expression Eq(object value)
        {
            string p = Expression.GetParamName();
            Expression exp = new Expression();
            exp.SqlText.Append(string.Format(" {0}=@{1} ",Name,p));
            exp.Parameters.Add(new Command.Parameter{ Name=p, 
                Value=Mappings.PropertyCastAttribute.CastValue(Table,Name, value)});
            return exp;
        }
        public Expression LtEq(object value)
        {
            string p = Expression.GetParamName();
            Expression exp = new Expression();
            exp.SqlText.Append(string.Format(" {0}<=@{1} ", Name, p));
            exp.Parameters.Add(new Command.Parameter { Name = p,
                Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, value)
            });
            return exp;
        }
        public Expression Lt(object value)
        {
            string p = Expression.GetParamName();
            Expression exp = new Expression();
            exp.SqlText.Append(string.Format(" {0}<@{1} ", Name, p));
            exp.Parameters.Add(new Command.Parameter { Name = p,
                Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, value)
            });
            return exp;
        }
        public Expression Gt(object value)
        {
            string p = Expression.GetParamName();
            Expression exp = new Expression();
            exp.SqlText.Append(string.Format(" {0}>@{1} ", Name, p));
            exp.Parameters.Add(new Command.Parameter { Name = p,
                                                       Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, value)
            });
            return exp;
        }
        public Expression GtEq(object value)
        {
            string p = Expression.GetParamName();
            Expression exp = new Expression();
            exp.SqlText.Append(string.Format(" {0}>=@{1} ", Name, p));
            exp.Parameters.Add(new Command.Parameter { Name = p, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, value) });
            return exp;
        }
        public Expression NotEq(object value)
        {
            string p = Expression.GetParamName();
            Expression exp = new Expression();
            exp.SqlText.Append(string.Format(" {0}<>@{1} ", Name, p));
            exp.Parameters.Add(new Command.Parameter { Name = p, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, value) });
            return exp;
        }
        public Expression In(System.Collections.IEnumerable values)
        {
            string p;
            int i = 0;
            Expression exp = new Expression();
            exp.SqlText.Append(" " + Name + " in (");
            foreach (object value in values)
            {
                p = Expression.GetParamName();
                if (i > 0)
                    exp.SqlText.Append(",");
                exp.SqlText.Append("@" + p);
                exp.Parameters.Add(new Command.Parameter { Name = p, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, value) });

                i++;
            }
            exp.SqlText.Append(" )");
            return exp;
        }
        public Expression In(FieldInfo field, Expression expression)
        {
            Expression exp = new Expression();
            string astable = "T" + Expression.GetParamName();
            exp.SqlText.Append(" " + Name + " in (select "+astable+"."+ field.Name+" from "+ field.Table+" "+astable);
            if (expression != null && expression.SqlText.Length > 0)
            {
                exp.SqlText.Append(" where " + expression.SqlText.ToString());
                exp.Parameters.AddRange(expression.Parameters);
            }
            exp.SqlText.Append(")");
            return exp;
        }
        public Expression NotIn(System.Collections.IEnumerable values)
        {
            string p;
            int i = 0;
            Expression exp = new Expression();
            exp.SqlText.Append(" " + Name + " not in (");
            foreach (object value in values)
            {
                p = Expression.GetParamName();
                if (i > 0)
                    exp.SqlText.Append(",");
                exp.SqlText.Append("@" + p);
                exp.Parameters.Add(new Command.Parameter { Name = p, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, value) });

                i++;
            }
            exp.SqlText.Append(")");
            return exp;
        }
        public Expression NotIn(FieldInfo field, Expression expression)
        {
            Expression exp = new Expression();
            string astable = "T" + Expression.GetParamName();
            exp.SqlText.Append(" " + Name + " not in (select " + astable + "." + field.Name + " from " + field.Table + " " + astable);
            if (expression != null && expression.SqlText.Length > 0)
            {
                exp.SqlText.Append(" where " + expression.SqlText.ToString());
                exp.Parameters.AddRange(expression.Parameters);
            }
            exp.SqlText.Append(")");
            return exp;
        }
        public Expression Between(object fromvalue, object tovalue)
        {
            string p, p1;
            p = Expression.GetParamName();
            p1 = Expression.GetParamName();
            Expression exp = new Expression();
            exp.SqlText.Append( string.Format(" {0} Between @{1} and @{2}", Name, p, p1));
            exp.Parameters.Add(new Command.Parameter { Name = p, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, fromvalue) });
            exp.Parameters.Add(new Command.Parameter { Name = p1, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, tovalue) });
            return exp;
        }
        public Expression NotBetween(object fromvalue, object tovalue)
        {
            string p, p1;
            p = Expression.GetParamName();
            p1 = Expression.GetParamName();
            Expression exp = new Expression();
            exp.SqlText.Append(string.Format(" {0} not Between @{1} and @{2}", Name, p, p1));
            exp.Parameters.Add(new Command.Parameter { Name = p, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, fromvalue) });
            exp.Parameters.Add(new Command.Parameter { Name = p1, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, tovalue) });
            return exp;
        }
        public Expression Like(object value)
        {
            if (value != null && value is System.Collections.IEnumerable && value.GetType() != typeof(string))
                return LikeMany((System.Collections.IEnumerable)value);
            string pn = Expression.GetParamName();
            Expression exp = new Expression();
            exp.SqlText.Append(
                string.Format("{0} like @{1}", Name, pn)
                );
            exp.Parameters.Add(new Command.Parameter { Name = pn, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, value) });
            return exp;
        }
        private Expression LikeMany(System.Collections.IEnumerable value)
        {
            
            Expression exp = new Expression();
            int i = 0;
            string pn;
            exp.SqlText.Append("(");
            foreach (object item in value)
            {
                pn = Expression.GetParamName();
                if (i > 0)
                    exp.SqlText.Append(" or ");
                exp.SqlText.Append(Name +" like @"+pn);
                exp.Parameters.Add(new Command.Parameter { Name = pn, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, item) });
                i++;
            }
            exp.SqlText.Append(")");
            return exp;
        }
        public Expression Match(object value)
        {
            if (value != null && value is System.Collections.IEnumerable && value.GetType() !=typeof(string))
                return MatchMany((System.Collections.IEnumerable)value);
            return Like("%" + value + "%");
        }
        private Expression MatchMany(System.Collections.IEnumerable value)
        {
            Expression exp = new Expression();
            int i = 0;
            string pn;
            exp.SqlText.Append("(");
            foreach (object item in value)
            {
                pn = Expression.GetParamName();
                if (i > 0)
                    exp.SqlText.Append(" or ");
                exp.SqlText.Append(Name + " like @" + pn);
                exp.Parameters.Add(new Command.Parameter { Name = pn, Value = "%"+item+"%" });
                i++;
            }
            exp.SqlText.Append(")");
            return exp;
        }
        public Expression IsNull()
        {
            Expression exp = new Expression();
            exp.SqlText.Append(" "+Name+" is null");
            return exp;
        }
        public Expression IsNotNull()
        {
            Expression exp = new Expression();
            exp.SqlText.Append(" " + Name + " is not null");
            return exp;
        }
        public override string ToString()
        {
            return Name;
        }
        public static Expression operator ==(FieldInfo field, object value)
        {
            if (value == null)
                return field.IsNull();
            if (value is System.Collections.IEnumerable && value.GetType() !=typeof(string))
                return field.In((System.Collections.IEnumerable)value);
            return field.Eq(value);
        }
        public static Expression operator !=(FieldInfo field, object value)
        {
            if (value == null)
                return field.IsNotNull();
            if (value is System.Collections.IEnumerable && value.GetType() != typeof(string))
                return field.NotIn((System.Collections.IEnumerable)value);
            return field.NotEq(value);
        }
        public static Expression operator >(FieldInfo field, object value)
        {
            return field.Gt(value);
        }
        public static Expression operator >=(FieldInfo field, object value)
        {
            return field.GtEq(value);
        }
        public static Expression operator <(FieldInfo field, object value)
        {
            return field.Lt(value);
        }
        public static Expression operator <=(FieldInfo field, object value)
        {
            return field.LtEq(value);
        }
        public FieldInfo At()
        {
            return new FieldInfo(Table, Table + "." + Name);
        }
        public FieldInfo At(string table)
        {
            return new FieldInfo(table, table + "." + Name);
        }

        public string Desc
        {
            get
            {
                return Name + " desc ";
            }
        }
        public string Asc
        {
            get
            {
                return Name + " asc ";
            }
        }
        public Field NewValue(object value)
        {

            return new Field { Name = Name, Value = Mappings.PropertyCastAttribute.CastValue(Table, Name, value) };
        }

        
    }
}
